//! $Id$
//!
//! @File    NamedProvider.h
//! @Author  Johan Andersson <skagget77@gmail.com>
//! @Date    Thu Oct 01 20:46 2009
//! @Ingroup IoCppDetail
//!

#if !defined(IOCPP_NAMEDPROVIDER_H)
#define IOCPP_NAMEDPROVIDER_H

#include "IoCpp/Injector.h"
#include "IoCpp/Detail/ResolverCache.h"

#include <boost/bind.hpp>
#include <boost/function.hpp>
#include <boost/ref.hpp>
#include <string>

namespace IoCpp
{
namespace Detail
{
   //!
   //! @author  Johan Andersson <skagget77@gmail.com>
   //! @date    Fri Oct 02 19:35 2009
   //! @ingroup IoCppDetail
   //!
   //! The NamedProvider class.
   //!
   template<class T1>
   class NamedProvider
   {
   public :
      //!
      //! Creates a new instance of the NamedProvider class.
      //!
      //! @param injector The container's Injector.
      //!
      //! @param name The provider's name.
      //!
      NamedProvider( const Injector& injector, const std::string& name );

      // Function provider.
      template<class T2>
      void SetProvider( T2 (*provider)() );

      template<class T2, class T3>
      void SetProvider( T2 (*provider)(T3) );

      template<class T2, class T3, class T4>
      void SetProvider( T2 (*provider)(T3, T4));

      template<class T2, class T3, class T4, class T5>
      void SetProvider( T2 (*provider)(T3, T4, T5));

      template<class T2, class T3, class T4, class T5, class T6>
      void SetProvider( T2 (*provider)(T3, T4, T5, T6) );

      template<class T2, class T3, class T4, class T5, class T6, class T7>
      void SetProvider( T2 (*provider)(T3, T4, T5, T6, T7) );

      // Function provider with injector.
      template<class T2>
      void SetProvider( T2 (*provider)( const Injector& ) );

      template<class T2, class T3>
      void SetProvider( T2 (*provider)( const Injector&, T3) );

      template<class T2, class T3, class T4>
      void SetProvider( T2 (*provider)( const Injector&, T3, T4));

      template<class T2, class T3, class T4, class T5>
      void SetProvider( T2 (*provider)( const Injector&, T3, T4, T5));

      template<class T2, class T3, class T4, class T5, class T6>
      void SetProvider( T2 (*provider)( const Injector&, T3, T4, T5, T6) );

      template<class T2, class T3, class T4, class T5, class T6, class T7>
      void SetProvider( T2 (*provider)( const Injector&, T3, T4, T5, T6, T7) );

      // Boost function provider.
      template<class T2>
      void SetProvider( boost::function<T2 ()> provider );

      template<class T2, class T3>
      void SetProvider( boost::function<T2 (T3)> provider );

      template<class T2, class T3, class T4>
      void SetProvider( boost::function<T2 (T3, T4)> provider );

      template<class T2, class T3, class T4, class T5>
      void SetProvider( boost::function<T2 (T3, T4, T5)> provider );

      template<class T2, class T3, class T4, class T5, class T6>
      void SetProvider( boost::function<T2 (T3, T4, T5, T6)> provider );

      template<class T2, class T3, class T4, class T5, class T6, class T7>
      void SetProvider( boost::function<T2 (T3, T4, T5, T6, T7)> provider );

      // Boost function provider with injector.
      template<class T2>
      void SetProvider( boost::function<T2 (const Injector&)> provider );

      template<class T2, class T3>
      void SetProvider( boost::function<T2 (const Injector&, T3)> provider );

      template<class T2, class T3, class T4>
      void SetProvider( boost::function<T2 (const Injector&, T3, T4)> provider );

      template<class T2, class T3, class T4, class T5>
      void SetProvider( boost::function<T2 (const Injector&, T3, T4, T5)> provider );

      template<class T2, class T3, class T4, class T5, class T6>
      void SetProvider( boost::function<T2 (const Injector&, T3, T4, T5, T6)> provider );

      template<class T2, class T3, class T4, class T5, class T6, class T7>
      void SetProvider( boost::function<T2 (const Injector&, T3, T4, T5, T6, T7)> provider );

      //!
      //! Commits the named provider to the specified ResolverCache.
      //!
      //! @param cache The ResolverCache to commit to.
      //!
      void Commit( ResolverCache& cache );

   private :
      // The container's injector.
      const Injector& injector;

      // The provider's name.
      std::string name;

      // The provider.
      boost::function<T1 ()> provider;
   };


   template<class T1>
   NamedProvider<T1>::NamedProvider( const Injector& injector, const std::string& name )
      : injector(injector), name(name) {
   }


   // Function provider.
   template<class T1>
   template<class T2>
   void NamedProvider<T1>::SetProvider( T2 (*provider)() )
   {
      this->provider = provider;
   }

   template<class T1>
   template<class T2, class T3>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(T3) )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(T3, T4) )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(T3, T4, T5) )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(T3, T4, T5, T6) )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6, class T7>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(T3, T4, T5, T6, T7) )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T7>, boost::cref(injector), name));
   }


   // Function provider with injector.
   template<class T1>
   template<class T2>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&) )
   {
      this->provider = boost::bind(provider, boost::cref(injector));
   }

   template<class T1>
   template<class T2, class T3>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&, T3) )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&, T3, T4) )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&, T3, T4, T5) )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&, T3, T4, T5, T6) )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6, class T7>
   void NamedProvider<T1>::SetProvider( T2 (*provider)(const Injector&, T3, T4, T5, T6, T7) )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T7>, boost::cref(injector), name));
   }


   // Boost function provider.
   template<class T1>
   template<class T2>
   void NamedProvider<T1>::SetProvider( boost::function<T2 ()> provider )
   {
      this->provider = provider;
   }

   template<class T1>
   template<class T2, class T3>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (T3)> provider )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (T3, T4)> provider )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (T3, T4, T5)> provider )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (T3, T4, T5, T6)> provider )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6, class T7>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (T3, T4, T5, T6, T7)> provider )
   {
      this->provider = boost::bind(provider,
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T7>, boost::cref(injector), name));
   }


   // Boost function provider with injector.
   template<class T1>
   template<class T2>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector));
   }

   template<class T1>
   template<class T2, class T3>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&, T3)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&, T3, T4)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&, T3, T4, T5)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&, T3, T4, T5, T6)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name));
   }

   template<class T1>
   template<class T2, class T3, class T4, class T5, class T6, class T7>
   void NamedProvider<T1>::SetProvider( boost::function<T2 (const Injector&, T3, T4, T5, T6, T7)> provider )
   {
      this->provider = boost::bind(provider, boost::cref(injector),
         boost::bind(&Injector::Inject<T3>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T4>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T5>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T6>, boost::cref(injector), name),
         boost::bind(&Injector::Inject<T7>, boost::cref(injector), name));
   }


   template<class T1>
   void NamedProvider<T1>::Commit( ResolverCache& cache ) {
      cache.AddProvider<T1>(name, provider);
   }
}
}

#endif   // IOCPP_NAMEDPROVIDER_H
